/*************************************************************************
	> File Name: client.cpp
	> Author: xuwenlong
	> Mail: myxuan475@126.com 
	> Created Time: 2018年09月17日 星期一 15时02分07秒
 ************************************************************************/
#include "user.h"
#include <event2/thread.h>
#include <map>

User::User(Server *server, ProtocolHandler *pHandler):
    m_ifd(-1),
    m_pbase(NULL),
    m_bev(NULL),
    m_pServer(server),
    m_bufferLock(true),
    m_pProtocolHandler(pHandler),
    m_pThread(threads::CreateThread("user",this))
{
    if (pHandler)
        pHandler->AppendTransport(this);
}

User::~User()
{
    stopWork();

    if (m_pbase) {
        event_base_free(m_pbase);
    }

    delete m_pProtocolHandler;
    threads::DeleteThread(m_pThread);
}

void User::RegisterTransport(ProtocolHandler *pHandler)
{
    m_pProtocolHandler = pHandler;
    if (pHandler)
        pHandler->AppendTransport(this);
}

BOOL User::OnReceiveJson(
        S32 index,
        DAS_RESULT status,
        const std::string &method,
        const Json::Value &result)
{
    return TRUE;
}

void User::threadMain()
{
    AutoLock lock(m_ilock);
    m_pbase = event_base_new();
    evthread_make_base_notifiable(m_pbase);
    m_bev = bufferevent_socket_new(
                m_pbase, m_ifd,
                BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
    if (m_bev == NULL) {
        LOG_ERR("bufferevent create socket failed");
//        m_cond_.Broadcast();
        return;
    }
    bufferevent_setcb(m_bev, DoRead, NULL, DoError, (void *)this);
//    bufferevent_setwatermark(m_bev, EV_READ, 0, MAX_RECV_SIZE);
    bufferevent_enable(m_bev, EV_READ);

//    evutil_make_listen_socket_reuseable_port(m_ifd);
//    evutil_make_socket_nonblocking(m_ifd);

    bufferevent_setfd(m_bev, m_ifd);
    bufferevent_enable(m_bev, EV_READ);

//    m_cond_.Broadcast();
    {
        AutoUnlock unlock(m_ilock);
        //阻塞
        event_base_dispatch(m_pbase);
    }while(0);
    LOG_INFO("pWork idx = %p Exit", this);
    CloseSession();
}

bool User::startWork(int fd,sockaddr *addr)
{
    m_ifd = fd,
    m_iaddr = *addr;
//    evutil_make_listen_socket_reuseable(m_ifd);
    if (!m_pThread->start())
        return false;
//    AutoLock lock(m_ilock);
//    m_cond_.Wait(lock);
    return true;
}

void User::stopWork()
{
    LOG_TRACE();
    {
        AutoLock lock(m_ilock);
        CloseSession();
        if (m_pbase) {
            event_base_loopbreak(m_pbase);
        }

        if (m_bev) {
            bufferevent_disable(m_bev,EV_READ | EV_WRITE);
            bufferevent_free(m_bev);
            shutdown(m_ifd,SHUT_RDWR);
            m_bev = NULL;
        }
    }while(0);
    m_pProtocolHandler->StopTimer();
    m_pThread->join();
}

void User::OnReceiveData(void *data, S32 bytes)
{
    m_pProtocolHandler->OnReceiveData(data,bytes);
    //m_pProtocolHandler->RpcParse();
}

BOOL User::SendData(const char *data, S32 bytes)
{
    AutoLock bevLock(m_bufferLock);
    if (!m_bev)
        return false;
    return 0 == bufferevent_write(m_bev,data,bytes);
}

BOOL User::IsTaskExit()
{
    return !m_pThread->is_running() && m_bev==NULL && m_ifd!=-1;
}

int User::SocketFd()
{
    AutoLock autolock(m_bufferLock);
    if (!m_bev)
        return 1;
    return bufferevent_getfd(m_bev);
}

void User::DoRead(bufferevent *bev, void *ctx)
{
    User *pWork = (User *)ctx;
    struct evbuffer* input = bufferevent_get_input(bev);
    size_t length = evbuffer_get_length(input);
    char *buf = (char*)malloc(length+1);
    evbuffer_remove(input, buf, length);
    buf[length] = 0;
    LOG_DEBUG("recv:%d bytes",length);
    pWork->OnReceiveData(buf,length);
    free(buf);
}

void User::DoError(bufferevent *bev, short ev, void *ctx)
{
    User* pWork = (User*)ctx;
    short err = 0;

    err = (BEV_EVENT_ERROR);
    while (ev & err) {
        pWork->CloseSession();
        LOG_ERR("something error,%d,%s.",
                (ev&err),
                evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
        break;
    }

    err = BEV_EVENT_CONNECTED;
    while (ev & err) {
        LOG_INFO("Client has been successfully connected.");
        break;
    }

    err = BEV_EVENT_TIMEOUT;
    while (ev & err) {
        LOG_ERR("timeout,%d,%s.",
                (ev&err),
                evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
        break;
    }

    err = BEV_EVENT_EOF|BEV_EVENT_READING|BEV_EVENT_WRITING;
    while (ev & err) {
        pWork->CloseSession();
        LOG_INFO("Connection closed,ev = %d,%s",
                 ev&err,
                 evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
        break;
    }

}

void User::CloseSession()
{
    AutoLock bevLock(m_bufferLock);
    if (m_bev) {
//        int fd = bufferevent_getfd(m_bev);
//        bufferevent_disable(m_bev,EV_READ);
//        bufferevent_enable(m_bev,EV_READ | EV_WRITE);
        bufferevent_free(m_bev);
//        shutdown(fd,SHUT_RDWR);
        evutil_closesocket(m_ifd);
        m_bev = NULL;
    }
}

Server *User::server()
{
    return m_pServer;
}
